package com.malachi.plugin.validateattr;

import com.malachi.plugin.validateattr.annotation.AttrValidate;
import com.malachi.plugin.validateattr.exception.AttrValidateException;
import com.malachi.plugin.validateattr.exception.InvalidDictAttrNameException;
import com.malachi.plugin.validateattr.exception.ValidateAdapterException;
import com.malachi.plugin.validateattr.service.DateValidateAdapter;
import com.malachi.plugin.validateattr.service.IDictService;
import com.malachi.plugin.validateattr.util.DateUtils;
import com.malachi.plugin.validateattr.util.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.text.ParseException;
import java.util.*;

/**
 * 验证属性工具类
 *
 * @param <T>
 */
public class ValidateAttrUtil<T> {
    private static final Logger log = LoggerFactory.getLogger(ValidateAttrUtil.class);


    private final IDictService dictService;

    public ValidateAttrUtil() {
        this.dictService = getDictService();
    }

    // 使用ServiceLoader加载实现类
    private IDictService getDictService() {
        ServiceLoader<IDictService> loader = ServiceLoader.load(IDictService.class);
        Iterator<IDictService> iterator = loader.iterator();
        if (iterator.hasNext()) {
            return iterator.next();
        }
        throw new RuntimeException("No implementation found for IDictService");
    }

    /**
     * 验证属性
     *
     * @param clazz 字节码对象
     * @param ado   实体对象
     * @return 验证结果
     */
    public String execute(Class<T> clazz, T ado) {
        StringBuilder sb = new StringBuilder();
        for (Field field : clazz.getDeclaredFields()) {
            AttrValidate attrValidate = field.getAnnotation(AttrValidate.class);
            if (null == attrValidate) {
                continue;
            }
            Object value;
            try {
                field.setAccessible(true);
                value = field.get(ado);
            } catch (IllegalAccessException e) {
                log.error("==========反射获取属性值失败 {}==========", field.getName());
                sb.append("系统错误！");
                break;
            }
            if (StringUtils.isNull(value)) {
                value = "";
            }
            handle(field, ado, value.toString(), attrValidate, sb);
        }
        return sb.toString();
    }

    /**
     * 验证属性
     *
     * @param clazz 字节码对象
     * @param ados  实体对象集合
     * @return 验证结果
     */
    public String executeList(Class<T> clazz, List<T> ados) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < ados.size(); i++) {
            T ado = ados.get(i);
            int startIndex = sb.length();
            for (Field field : clazz.getDeclaredFields()) {
                AttrValidate attrValidate = field.getAnnotation(AttrValidate.class);
                if (null == attrValidate) {
                    continue;
                }
                Object value;
                try {
                    field.setAccessible(true);
                    value = field.get(ado);
                } catch (IllegalAccessException e) {
                    log.error("==========反射获取属性值失败 {}==========", field.getName());
                    sb.append("系统错误！");
                    break;
                }
                if (StringUtils.isNull(value)) {
                    value = "";
                }
                handle(field, ado, value.toString(), attrValidate, sb);
            }
            int endIndex = sb.length();
            if (endIndex > startIndex) {
                if (startIndex == 0) {
                    sb.insert(startIndex, "第" + (i + 1) + "行：");
                } else {
                    sb.insert(startIndex, "<br/>第" + (i + 1) + "行：");
                }
            }
        }
        return sb.toString();
    }

    /**
     * 验证属性
     *
     * @param field        属性
     * @param ado          操作对象
     * @param value        属性值
     * @param attrValidate 验证规则
     * @param sb           错误信息对象
     */
    private void handle(Field field, T ado, String value, AttrValidate attrValidate, StringBuilder sb) {
        String name = attrValidate.name();
        boolean required = attrValidate.required();
        int maxLength = attrValidate.maxLength();
        boolean positive = attrValidate.positive();
        boolean positiveInteger = attrValidate.positiveInteger();
        long maxValue = attrValidate.maxValue();
        long minValue = attrValidate.minValue();
        // 属性值不为空
        if (StringUtils.isNotEmpty(value)) {
            AttrValidate.TypeEnum type = attrValidate.type();
            switch (type) {
                case STRING:
                    if (value.length() > maxLength) {
                        sb.append(name).append("超出长度").append(maxLength).append(",");
                    }
                    break;
                case INTEGER:
                    // 验证是否为Integer类型
                    try {
                        int parseInt = Integer.parseInt(value);
                        // 验证是否为正整数
                        if (positiveInteger && parseInt < 0) {
                            sb.append(name).append("必须为正整数").append(",");
                        }
                        // 验证最大值、最小值
                        else if (parseInt > maxValue || parseInt < minValue) {
                            sb.append(name).append("必须小于").append(maxValue).append("且大于").append(minValue).append(",");
                        }
                    } catch (NumberFormatException e) {
                        sb.append(name).append("必须为整形数字").append(",");
                    }
                    break;
                case LONG:
                    // 验证是否为Long类型
                    try {
                        long parseLong = Long.parseLong(value);
                        // 验证是否为正整数
                        if (positiveInteger && parseLong < 0) {
                            sb.append(name).append("必须为正整数").append(",");
                        }
                        // 验证最大值、最小值
                        else if (parseLong > maxValue || parseLong < minValue) {
                            sb.append(name).append("必须小于").append(maxValue).append("且大于").append(minValue).append(",");
                        }
                    } catch (NumberFormatException e) {
                        sb.append(name).append("必须为整形数字").append(",");
                    }
                    break;
                case BIGDECIMAL:
                    // 验证是否为BigDecimal类型
                    int scale = attrValidate.scale();
                    try {
                        BigDecimal bigDecimal = new BigDecimal(value);
                        // 验证是否为正数
                        if (positive && bigDecimal.compareTo(BigDecimal.ZERO) < 0) {
                            sb.append(name).append("必须为正数").append(",");
                        }
                        // 验证最大值、最小值
                        else if (bigDecimal.compareTo(new BigDecimal(maxValue)) > 0 || bigDecimal.compareTo(new BigDecimal(minValue)) < 0) {
                            sb.append(name).append("必须小于").append(maxValue).append("且大于").append(minValue).append(",");
                        }
                        // 验证小数位数[去除无效小数]
                        else if (bigDecimal.stripTrailingZeros().scale() > scale) {
                            sb.append(name).append("小数位数不能超过").append(scale).append("位,");
                        }
                    } catch (NumberFormatException e) {
                        sb.append(name).append("必须为数字").append(",");
                    }
                    break;
                case DATE:
                    // 验证是否为Date类型
                    String pattern = attrValidate.datePattern();
                    Date date = null;
                    try {
                        date = DateUtils.dateTime(value, pattern);
                    } catch (ParseException e) {
                        sb.append(name).append("不是合法日期格式").append(pattern).append(",");
                    }
                    if (Objects.nonNull(date) && !Objects.equals(attrValidate.dateValidateAdapter(), DateValidateAdapter.class)) {
                        try {
                            Object instance = attrValidate.dateValidateAdapter().newInstance();
                            Method formatMethod = attrValidate.dateValidateAdapter().getMethod("validate", new Class[]{Date.class});
                            String errMsg = (String) formatMethod.invoke(instance, date);
                            if (StringUtils.isNotEmpty(errMsg)) {
                                sb.append(name).append(errMsg).append(",");
                            }
                        } catch (InstantiationException | IllegalAccessException e) {
                            throw new ValidateAdapterException("系统错误(实例化日期适配器错误)！", e);
                        } catch (NoSuchMethodException | InvocationTargetException e) {
                            throw new ValidateAdapterException("系统错误(日期适配器方法调用失败)！", e);
                        }
                    }
                    break;
                case BOOLEAN:
                    // 验证是否为Boolean类型
                    if (!"true".equals(value) && !"false".equals(value)) {
                        sb.append(name).append("不是布尔类型").append(",");
                    }
                    break;
                case ENUM:
                    // 验证是否为ENUM类型
                    String classPath = attrValidate.classPath();
                    try {
                        Class<?> aClass = Class.forName(classPath);
                        if (aClass.isEnum()) {
                            boolean flag = false;
                            Enum[] enums = (Enum[]) aClass.getEnumConstants();
                            for (Enum anEnum : enums) {
                                Method getValue = aClass.getMethod("getValue");
                                if (Objects.equals(getValue.invoke(anEnum).toString(), value)) {
                                    flag = true;
                                    break;
                                }
                            }
                            if (!flag) {
                                sb.append(name).append("取值不合法！");
                            }
                        }
                    } catch (ClassNotFoundException | NoSuchMethodException e) {
                        log.error("==========枚举类不存在 或 枚举类没有getValue方法==========");
                        throw new AttrValidateException("类未找到|方法不存在！", e);
                    } catch (InvocationTargetException | IllegalAccessException e) {
                        log.error("验证器处理枚举类型数据发生异常！", e);
                        throw new AttrValidateException("系统错误！", e);
                    }
                    break;
                case DICT:
                    String dictType = attrValidate.dictType();
                    String valueType = attrValidate.dictValueType();
                    if ("value".equals(valueType)) {
                        if (StringUtils.isEmpty(dictService.getDictLabel(dictType, value))) {
                            sb.append(name).append("取值[").append(value).append("]不合法！");
                        }
                    } else if ("label".equals(valueType)) {
                        String dictValue = dictService.getDictValue(dictType, value);
                        if (StringUtils.isEmpty(dictValue)) {
                            sb.append(name).append("取值[").append(value).append("]不合法！");
                        } else {
                            // 需要转换取值
                            try {
                                field.set(ado, dictValue);
                            } catch (IllegalAccessException e) {
                                log.error("字典属性值 {} 赋值发生异常！", value, e);
                            }
                        }
                    } else {
                        log.error("字典数据类型 {} 不存在", valueType);
                        throw new InvalidDictAttrNameException("字典数据类型" + valueType + "不存在");
                    }
                    break;
                case FIXEDVALUE:
                    String fixedValue = attrValidate.fixedValue();
                    if (!Objects.equals(fixedValue, value)) {
                        sb.append(name).append("取值[").append(value).append("]不合法(").append(fixedValue).append(")！");
                    }
                    break;
                default:
                    log.warn("==========未知类型 {}==========请完善相关验证逻辑！！！", type);
                    break;
            }
        }
        // 属性值为空 并且 必填
        else if (required) {
            sb.append(name).append("不能为空").append(",");
        }
    }
}
